# !/usr/bin/env python
import os
import sys
import socket
import struct
import threading
import time
import hashlib
import traceback

reload(sys)
sys.setdefaultencoding("utf-8")

CUR_PATH = os.path.join(os.getcwd(), os.path.dirname(__file__))
TEST_MODE = True
if not TEST_MODE:
    RUN_PATH = "/opt"
    LISTEN_HOST = '12.3.4.5'
else:
    RUN_PATH = "/opt/test"
    LISTEN_HOST = '0.0.0.0'

# server port
LISTEN_PORT = 13431
# buffer size
BUF_SIZE = 512
# server log file
LOG_FILE = '/tmp/ftp_server.log'
# time format
ISOTIMEFORMAT = '%Y-%m-%d %X'

# HEAD STRUCT
HEAD_STRUCT = '128sIq32s'

MAX_CLIENT_NUM = 10
CUR_CLIENT_NUM = 0

VERSION_R00F07 = '00'
VERSION_R01F00 = '11'

def record_log(level, msg):
    # print('[' + level + ']   ' + msg)
    log_time = time.strftime('[%Y-%m-%d-%H-%M-%S]', time.localtime(time.time()))
    logfile = os.path.normpath(LOG_FILE)
    fp = open(logfile, 'a+')
    if not fp:
        return
    try:
        msg = '[' + level + ']   ' + log_time + '   ' + msg
        fp.write(msg + "\n")
    except Exception, e:
        record_log('error', str(e))
    finally:
        fp.close()
    pass


def get_run_file_name():
    # 遍历RUN_PATH下所有run文件
    run_file = {}
    try:
        files = os.listdir(RUN_PATH)
        for fi in files:
            tmp_file = os.path.join(RUN_PATH, fi)
            if os.path.isfile(tmp_file) and fi.endswith('.run'):
                if fi.startswith('centos'):
                    run_file[VERSION_R00F07] = tmp_file
                elif fi.startswith('01'):
                    run_file[VERSION_R01F00] = tmp_file
                else:
                    pass
    except:
        record_log('ERROR', str(traceback.format_exc()))

    return run_file

def get_run_files_info():
    # 遍历RUN_PATH下所有run文件
    run_files_info = {}
    try:
        files = os.listdir(RUN_PATH)
        for fi in files:
            tmp_file = os.path.join(RUN_PATH, fi)
            file_name, file_name_len, file_size, file_md5 = get_file_info(tmp_file)
            file_info = {'file_name': file_name,
                        'file_name_len': file_name_len,
                        'file_size': file_size,
                        'file_md5': file_md5,
                        'file_path': tmp_file}
            if os.path.isfile(tmp_file) and fi.endswith('.run'):
                if fi.startswith('11'):
                    run_files_info[VERSION_R00F07] = file_info
                elif fi.startswith('222'):
                    run_files_info[VERSION_R01F00] = file_info
                else:
                    pass
    except:
        record_log('ERROR', str(traceback.format_exc()))

    return run_files_info


def unpack_file_info(file_info):
    file_name, file_name_len, file_size, file_md5 = struct.unpack(HEAD_STRUCT, file_info)
    file_name = file_name[:file_name_len]
    return file_name, file_size, file_md5


def get_md5(input_file):
    """
    get file md5 value
    """
    rb = open(input_file, 'rb')
    md5 = hashlib.md5()
    md5.update(rb.read())
    rb.close()
    return md5.hexdigest()


def get_file_info(file_path):
    # record file info
    file_name = os.path.basename(file_path)
    file_name_len = len(file_name)
    file_size = os.path.getsize(file_path)
    file_md5 = get_md5(file_path)
    return file_name, file_name_len, file_size, file_md5


def tranform_file(conn, addr, run_files_info):
    global CUR_CLIENT_NUM
    while True:
        if CUR_CLIENT_NUM < MAX_CLIENT_NUM:
            break
        record_log('INFO', 'ftp client num is limit, addr is %s, then sleep' % str(addr))
        time.sleep(60)
        continue
    CUR_CLIENT_NUM += 1
    record_log('INFO', 'tranform start, num is %s, addr is %s' % (CUR_CLIENT_NUM, str(addr)))
    try:
        file_info_size = struct.calcsize(HEAD_STRUCT)
        run_file_info = run_files_info.get(VERSION_R00F07)
        if not run_file_info:
            run_file_info = run_files_info[VERSION_R01F00]
        fhead = struct.pack(HEAD_STRUCT, run_file_info['file_name'],
            run_file_info['file_name_len'], run_file_info['file_size'], run_file_info['file_md5'])
        # success flag
        flag = False
        # log file
        rec_time = time.strftime(ISOTIMEFORMAT, time.localtime())
        conn.send(fhead)
        old_info = conn.recv(file_info_size)
        fname, fsize, old_md5 = unpack_file_info(old_info)
        if fname == 'error':
            run_file_info = run_files_info[VERSION_R01F00]
            fhead = struct.pack(HEAD_STRUCT, run_file_info['file_name'],
                run_file_info['file_name_len'], run_file_info['file_size'], run_file_info['file_md5'])
            conn.send(fhead)
            old_info = conn.recv(file_info_size)
            fname, fsize, old_md5 = unpack_file_info(old_info)

        run_file = run_file_info['file_path']
        record_log('INFO', 'start tranform [ %s ].....  recvTime: %s' % (str(run_file_info['file_name']),str(rec_time)))
        try:
            # open file
            with open(run_file, 'r') as f:
                # seek file new pos
                f.seek(fsize, 0)
                while True:
                    try:
                        block = f.read(BUF_SIZE)
                        if len(block) == 0:
                            flag = True
                            break
                        # send file data
                        conn.send(block)
                    except:
                        record_log('ERROR', str(traceback.format_exc()))
                        break
            f.close()
        except:
            record_log('ERROR', str(traceback.format_exc()))

        if flag:
            record_log('INFO', ' successfully transform run file , md5: ' + run_file_info['file_md5'])
        else:
            record_log('INFO', ' failed transform run file')
        conn.close()
    except:
        record_log('ERROR', str(traceback.format_exc()))
    CUR_CLIENT_NUM -= 1
    record_log('INFO', 'tranform end, num is %s, addr is %s' % (CUR_CLIENT_NUM, str(addr)))


def ftp_server():
    record_log('INFO', 'Calculating file information, please wait.')
    run_files_info = get_run_files_info()
    record_log('INFO', 'ftp server start, binding port %s' % LISTEN_PORT)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # define socket type
    s.bind((LISTEN_HOST, LISTEN_PORT))  # bind host and port
    s.listen(5)  # start listen
    while True:
        try:
            conn, addr = s.accept()  # accept tcp connect and return new socket, ip
            print "conn", addr
            record_log('INFO', 'ftp client addr is %s' % str(addr))
            thread = threading.Thread(target=tranform_file, args=(conn, addr, run_files_info))
            thread.start()
        except:
            time.sleep(5)
            record_log('ERROR', str(traceback.format_exc()))
    # s.close()


if __name__ == '__main__':
    if os.path.exists(LOG_FILE):
        os.remove(LOG_FILE)

    if len(sys.argv) == 1:
        ftp_server()
        sys.exit(0)
    elif sys.argv[1] == '-h' or sys.argv[1] == '--help':
        print 'usage: python %s' % sys.argv[0]
        sys.exit(-1)
    else:
        sys.exit(0)